package com.base.test.mutual.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.base.commons.tools.constant.Constant;
import com.base.commons.tools.exception.BaseOfException;
import com.base.commons.tools.utils.CameTools;
import com.base.commons.tools.utils.DateUtils;
import com.base.commons.tools.utils.HttpConnectUtil;
import com.base.commons.tools.utils.StringUtils;
import com.base.test.mutual.dto.*;
import com.base.test.mutual.exception.ModuleErrorCode;
import com.base.test.mutual.properties.BscProperties;
import com.base.test.mutual.redis.BscTokenRedis;
import lombok.extern.log4j.Log4j2;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.*;

import static com.base.commons.tools.utils.DateUtils.format;

/**
 * @author carl
 * @since 2020/1/19
 */
@Log4j2
@Configuration
@EnableConfigurationProperties(BscProperties.class)
public class BSCHttpUtils {

    @Autowired
    private BscProperties bscProperties;

    @Autowired
    private BscTokenRedis bscTokenRedis;

    @Autowired
    private HttpServletRequest httpServletRequest;

    private String defaultSys = "bms";

    private static Map<String, BscProperties.SysConfig> sysConfigMap = new HashMap<>();

    @PostConstruct
    public void initSysConfigs(){
        List<BscProperties.SysConfig> sysConfigList = bscProperties.getSysConfigs();
        for(BscProperties.SysConfig sc:sysConfigList){
            sysConfigMap.put(sc.getSysCode(),sc);
        }
    }

    public  <T> T executePhpToken(Object requestObj, String url, String method, Class<?> clazz,String... sysCode){
        BscProperties.SysConfig sysConfig = getSysConfig(sysCode);
        BscJwtToken token= bscTokenRedis.get("php"+sysConfig.getAppId()+sysConfig.getAppKey());
        if(token==null){
            token= generatePhpToken();
            bscTokenRedis.set(token,"php"+sysConfig.getAppId()+sysConfig.getAppKey());
        }
        if(token!=null&&isTokenExpired(token.getExpiration())){
            token= generatePhpToken();
            bscTokenRedis.set(token,"php"+sysConfig.getAppId()+sysConfig.getAppKey());
        }

        Object res =newPhpExecute(requestObj, url, token.getToken(),method, clazz);
        //如果返回401表示未授权
        return (T) res;
    }

    public  <T> T newPhpExecute(T requestObj, String url, String token, String method, Class<?> clazz){
        //String language = httpServletRequest.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
        url=bscProperties.getPhpUrl()+url;
        HttpHeaders headers = new HttpHeaders();
        if(StringUtils.isNotEmpty(token)){
            headers.add(HttpHeaders.AUTHORIZATION,token);
        }
        headers.add(HttpHeaders.ACCEPT_LANGUAGE,"zh-cn");
        log.info("模块Module:{},url:{},请求参数{}","BSC",url,JSON.toJSONString(requestObj));
        ResponseEntity res = HttpConnectUtil.tempHttp(requestObj, url, headers, method, new HashMap<>(), clazz);
        log.info("模块Module:{},url:{},请求结果：{}","BSC",url,res.getBody());
        return (T) res.getBody();
    }

    public  <T> T executePhpGatewayToken(Object requestObj, String url, String method, Class<?> clazz,String... sysCode){
        BscProperties.SysConfig sysConfig = getSysConfig(sysCode);
        BscJwtToken token= bscTokenRedis.get("php"+sysConfig.getAppId()+sysConfig.getAppKey());
        if(token==null){
            token= generatePhpToken();
            bscTokenRedis.set(token,"php"+sysConfig.getAppId()+sysConfig.getAppKey());
        }
        if(token!=null&&isTokenExpired(token.getExpiration())){
            token= generatePhpToken();
            bscTokenRedis.set(token,"php"+sysConfig.getAppId()+sysConfig.getAppKey());
        }

        Object res =newPhpExecute(requestObj, url, "Bearer "+token.getToken(),method, clazz);
        //如果返回401表示未授权
        return (T) res;
    }
    public  <T> T executeToken(Object requestObj, String url, String method, Class<?> clazz,String... sysCode){
        BscProperties.SysConfig sysConfig = getSysConfig(sysCode);
        BscJwtToken token= bscTokenRedis.get(sysConfig.getAppId()+sysConfig.getAppKey());
        if(token==null){
            token= generateToken();
            bscTokenRedis.set(token,sysConfig.getAppId()+sysConfig.getAppKey());
        }
        if(token!=null&&isTokenExpired(token.getExpiration())){
            token= generateToken();
            bscTokenRedis.set(token,sysConfig.getAppId()+sysConfig.getAppKey());

        }

        Object res =newExecute(requestObj, url, token.getToken(),method, clazz);
        //如果返回401表示未授权
        return (T) res;
    }

    public  <T> T newExecute(T requestObj, String url, String token, String method, Class<?> clazz){
        String request = null;
        ResponseEntity res=null;
        try {
            //String language = httpServletRequest.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
            url=bscProperties.getUrl()+url;
            HttpHeaders headers = new HttpHeaders();
            if(StringUtils.isNotEmpty(token)){
                headers.add(HttpHeaders.AUTHORIZATION,token);
            }
            headers.add(HttpHeaders.ACCEPT_LANGUAGE,"zh-cn");
            request = CameTools.toUnderlineJSONString(requestObj);
            log.info("模块Module:{},url:{},请求参数[{}],请求结果：{}","BSC",url,request);
            res = HttpConnectUtil.tempHttp(request, url, headers, method, new HashMap<>(), clazz);
            log.info("模块Module:{},url:{},请求结果：{}","BSC",url,res.getBody());
        }catch (Exception e) {
            log.error("模块Module:{},url:{},请求参数[{}],异常信息：{}","BSC",url,request,e);
            throw new BaseOfException(ModuleErrorCode.FMS_FAILED,e);
        }
        return (T) res.getBody();
    }

    /**
     * bsc 远程接口
     * @param requestObj 请求参数
     * @param url 接口路径
     * @param method 方法
     * @param clazz 响应结果的类型
     * @param <T> 响应结果的类型
     * @return
     */
    public  <T> T execute(T requestObj, String url, String token, String method, Class<?> clazz){
        BSCReqData requestParam= new BSCReqData();
        requestParam.setRequestId(UUID.randomUUID().toString());
        requestParam.setRequestTime(format(new Date(),DateUtils.DATE_TIME_PATTERN));
        requestParam.setLanguage(bscProperties.getLanguage());
        requestParam.setRequestData(requestObj);
        //入参转化下划线
        url=bscProperties.getUrl()+url;
        HttpHeaders headers = new HttpHeaders();
        headers.add("Authorization",token);
        log.info("模块Module:{},url:{},请求参数[{}]","BSC",url,JSON.toJSONString(requestParam));
        ResponseEntity res = HttpConnectUtil.tempHttp(requestParam, url, headers, method, new HashMap<>(), clazz);
        log.info("模块Module:{},url:{},请求参数[{}],请求结果：{}","BSC",url,JSON.toJSONString(requestParam),res.getBody());
        return (T) res.getBody();
    }

    /**
     * bsc登录跳转
     * @return
     */
    public BscAuthToken generateToken(AuthLoginDTO authLoginDTO, String url){
        BscRespData<BscAuthToken> resp = null;
        Object  responseJson = newPhpExecute(JSONObject.toJSON(authLoginDTO), url, null,"post", String.class);
        resp = JSON.parseObject(responseJson.toString(),new TypeReference<BscRespData<BscAuthToken>>(){});
        if(Constant.FAIL==resp.getResponseResult().byteValue()){
            throw new BaseOfException(resp.getResponseError().getMessage());
        }
        BscAuthToken bscAuthToken=resp.getResponseData();
        bscAuthToken.setExpiration(DateTime.now().plusSeconds(bscAuthToken.getExpireIn()).toDate());
        return bscAuthToken;
    }

    /**
     * bsc生成token
     * @return
     */
    public BscJwtToken generateToken(){
        Map<String, Object> mp = getBscAccessToken();
        BscJwtToken bscJwtToken =new BscJwtToken();
        bscJwtToken.setToken("Bearer"+" "+mp.get("access_token").toString());
        bscJwtToken.setExpiration(DateTime.now().plusSeconds(Integer.parseInt(mp.get("expire_in").toString())).toDate());
        return bscJwtToken;
    }

    /**
     * bsc php模块生成token
     * @return
     */
    public BscJwtToken generatePhpToken(){
        Map<String, Object> mp = getBscAccessToken();
        BscJwtToken bscJwtToken =new BscJwtToken();
        bscJwtToken.setToken(mp.get("access_token").toString());
        bscJwtToken.setExpiration(DateTime.now().plusSeconds(Integer.parseInt(mp.get("expire_in").toString())).toDate());
        return bscJwtToken;
    }

    /**
     * 调bsc， 获取access token
     * @return
     */
    private Map<String, Object> getBscAccessToken(String... sysCode) {
        BscProperties.SysConfig sysConfig = getSysConfig(sysCode);
        BscGetTokenReqData reqData=new BscGetTokenReqData();
        reqData.setAppId(sysConfig.getAppId());
        reqData.setAppKey(sysConfig.getAppKey());
        String url = bscProperties.getUrl() + bscProperties.getRoutesGetAccessToken();
        ResponseEntity responseEntity = HttpConnectUtil.tempHttp(reqData, url,
                null, "post", new HashMap<>(), Map.class);
        Map<String, Object> res = (Map) responseEntity.getBody();
        log.info("模块Module:{},url:{},请求参数[{}],请求结果：{}", "BSC", url, JSON.toJSONString(reqData), responseEntity.getBody());
        return (Map<String, Object>) res.get("data");
    }

    /**
     * token是否过期
     * @return  true：过期
     */
    public boolean isTokenExpired(Date expiration) {
        return expiration.before(new Date());
    }

    /**
     * 获取不同系统配置
     * @param sysCode
     * @return
     */
    public BscProperties.SysConfig getSysConfig(String... sysCode){
        BscProperties.SysConfig sysConfig = sysConfigMap.get(defaultSys);
        if(null!=sysCode&&sysCode.length>0){
            sysConfig = sysConfigMap.get(sysCode[0]);
        }
        return sysConfig;
    }
}
